﻿/* Filename: BattleController.as 
 *
 * Description: This class runs the in-game
 * battles between two players (one can be
 * an AI enemy).
 *
 * Created By: Tyler Samson
 * Feb 2008
 * @ Sierra Nevada College
 */
class BattleController {

	private var _player1:Player;
	private var _player2:Player;
	
	// *** Depths *** //
	/* Depths must be kept track of because no two
	 * objects can occupy the same depth.  The newer
	 * one will replace the older one. */
	private var _player1MapDepth:Number = 300;			// 300 - 312 are map tiles
	private var _player2MapDepth:Number = 100;			// 100 - 112 are map tiles
	private var _player1DemonsDepth:Number = 400;		// 400 - 412 are demons on tiles
	private var _player2DemonsDepth:Number = 200;		// 200 - 212 are demons on tiles
	private var _playerMenuDepth:Number = 5000;			// depth of player menu
	private static var _leftDemonDepth:Number = 4000;	// left demon picture depth
	private static var _rightDemonDepth:Number = 4031;	// right demon picture depth
	
	// *** Map Variables *** //
	// Used to setup the location of the maps.
	private var _player1MapX:Number = 210;				// origin of map (top left)
	private var _player1MapY:Number = 305;				// origin of map (top left)
	private var _player2MapX:Number = 210;				// origin of map (top left)
	private var _player2MapY:Number = 10;				// origin of map (top left)
	
	private var _initiative = 20;
	private var _initiativeList:InitiativeList;			// keep track of the demons and their play order
	private var _currentState:CurrentState;				// keep all information pertaining to the state of the battle
	private var _counter = 3;
	
	// *** End of Round Actions *** //
	// Variables used to hold information for the end of a round.
	private var _waitingDemons:Array = new Array();
	
	public function BattleController(p_player1:Player, p_player2:Player,
									 p_mapPlayer1:Map, p_mapPlayer2:Map) {
		
		_currentState = new CurrentState();
		_currentState._statusTypes = new EnumeratedType("attack", "terrainAttack", "terrainDone", "move", "item", p_player1.getName(), p_player2.getName());
					
		_player1 = p_player1;
		_player2 = p_player2;
		
		_player1.setMap(p_mapPlayer1);
		_player2.setMap(p_mapPlayer2);
		
		p_mapPlayer1.addDemons(_player1.getActiveDemons());
		p_mapPlayer2.addDemons(_player2.getActiveDemons());
		
		p_mapPlayer1.drawMap(_player1MapDepth, _player1DemonsDepth, _player1MapX, _player1MapY);
		p_mapPlayer2.drawMap(_player2MapDepth, _player2DemonsDepth, _player2MapX, _player2MapY);
		
		
		_initiativeList = new InitiativeList(_player1.getActiveDemons(), _initiative);
		_initiativeList.addArrayToHash(_player2.getActiveDemons(), _initiative);
		//var _objects:Array = _initiativeList.getAllObjects();
		while(!_initiativeList.isEnd()) {
			var _nextNode = _initiativeList.getCurrentNode();
			trace("ID: " + _nextNode.getUniqueId());
		}
		
		nextTurn();	// the game takes place in rounds, this sets up the next round
	}
	
	public function finished() {
		removeEverything();
		removeBigPictures();
		if (_currentState._attackDone == true && _currentState._moveDone == true) {
			nextTurn();
		} else {
			var _status:Number = _currentState._status;
			if (_status == _currentState._statusTypes["ai"]) {
				PlayerMenu.createPlayerMenu(_playerMenuDepth, _player2, _currentState._currentDemon, !_currentState._attackDone, !_currentState._moveDone, "right");
				rightDemon(_currentState._currentDemon, _currentState._currentTile);
				//ai();
				//nextTurn();
			} else {
				PlayerMenu.createPlayerMenu(_playerMenuDepth, _player1, _currentState._currentDemon, !_currentState._attackDone, !_currentState._moveDone, "left");
				leftDemon(_currentState._currentDemon, _currentState._currentTile);
			}
		}
	}
	
	// Set up the next turn of play
	public function nextTurn():Void {
		_currentState.printState();
		removeEverything();
		removeBigPictures();
		_currentState._attackDone = false;
		_currentState._moveDone = false;
		
		if (_initiativeList.isEnd()) {
			nextRound();
		}
		
		var _string = _initiativeList.listToString();
		trace(_string);
		
		/* 
		 * Find next demon in initiativeList, set the
		 * status to that player's turn.  If the player
		 * is AI, run AI.  Otherwise, the opens the menu
		 * for the player's demon and waits for input.
		 */
		var _nextDemon:Demon = _initiativeList.getCurrentNode();
		setCurrentState(_nextDemon);
		
		var _status:Number = _currentState._status;

		
		PlayerMenu.removePlayerMenu();
		if (_status == _currentState._statusTypes["ai"]) {
			PlayerMenu.createPlayerMenu(_playerMenuDepth, _player2, _nextDemon, !_currentState._attackDone, !_currentState._moveDone, "right");
			rightDemon(_nextDemon, _currentState._currentTile);
			//ai();
			//nextTurn();
		} else {
			PlayerMenu.createPlayerMenu(_playerMenuDepth, _player1, _nextDemon, !_currentState._attackDone, !_currentState._moveDone, "left");
			leftDemon(_nextDemon, _currentState._currentTile);
		}
	}
	
	public function nextRound() {
		_player1.getMap().decrementTiles();
		_player2.getMap().decrementTiles();
		
		while (_waitingDemons.length >= 1) {
			var _nextDemon = _waitingDemons.pop();
			var _playerMap:Map = _nextDemon.getOwner().getMap();
			var _tile = _playerMap.getTile(_nextDemon.getX(), _nextDemon.getY());
			
			_tile.addDemon(_nextDemon);
			_playerMap.redrawDemon(_tile);
			_initiativeList.addToHash(_nextDemon, _initiative);
		}
	}
	
	public function setCurrentState(p_demon:Demon):Void {
		_currentState._currentDemon = p_demon;
		_currentState._player = p_demon.getOwner();
		_currentState._playerMap = p_demon.getOwner().getMap();
		_currentState._currentTile = _currentState._playerMap.getTile(p_demon.getX(), p_demon.getY());
		_currentState._status = _currentState._statusTypes[p_demon.getOwner().getName()];
		_currentState._currentPlayer = p_demon.getOwner().getName();
		
		// Set the enemy
		var _enemy:Player;
		if (p_demon.getOwner().getName() == _player1.getName()) {
			_enemy = _player2;
		} else {
			_enemy = _player1;
		}
		
		_currentState._enemy = _enemy;
		_currentState._enemyMap = _enemy.getMap();
	}
	
	public function ai():Void {
		// RUN AI ROUTINES
		trace("*** AI DONE ***");
	}
	
	// *** Action Functions *** //
	/*  */
	
	public function dealDamage(p_tile:Tile, p_attack:Attack) {
		var _damage:Number = p_attack.getDamage();			// get the amount of damage the attack will do
		var _hit:Number = p_attack.isHitSuccessful();
		var _critical:Boolean = _hit == Attack._accuracyTypes["critical"];
		var _success:Boolean = _hit == Attack._accuracyTypes["hit"];
		var _typeBonus:Boolean = _currentState._currentTile.isElementType(p_attack.getElementType());
		var _typeAdvantage:Boolean = _currentState._currentDemon.hasTypeAdvantage(p_tile.getDemon().getElementType());
		trace(" hit " + _hit + "\n critical: " + _critical + "\n success: " + _success +
			  "\n type advantage: " + _typeAdvantage + "\n type bonus: " + _typeBonus + "\n damage: " + _damage);
		
		if (_typeBonus) {
			_damage + 5;
		}
		
		if (_critical && _typeAdvantage) {
			_damage = _damage*3;
		} else if (_critical || _typeAdvantage){
			_damage = _damage*2;
		}
		
		trace(" new damage: " + _damage);
		
		if (_success) {
			p_tile.getDemon().changeHealth(_damage);	// change the health to reflect damage taken
			rightDemon(p_tile.getDemon());				// refresh the side screen that shows hp
			if (p_tile.getDemon().getHealth() <= 0) {
				deadDemon(p_tile);
				removeDemonSelection("Right");
			}
		}
		
		_currentState._attackDone = true;
		finished();
	}
	
	private function deadDemon(p_tile:Tile) {
		var _oldDemon = p_tile.getDemon();
		var _player:Player = _oldDemon.getOwner();
		var _newDemon = _player.swapDeadDemon(_oldDemon);
		
		p_tile.removeDemon();
		_player.getMap().removeDemon(_oldDemon);
		_initiativeList.removeObject(_oldDemon);
		
		if (_newDemon) {
			_newDemon.setX(_oldDemon.getX());
			_newDemon.setY(_oldDemon.getY());
			_waitingDemons.push(_newDemon);
		}
	}
	
	private function terrainChange(p_tile:Tile, p_attack:TerrainAttack) {
		p_tile.setNewType(p_attack.getElementType());
		p_tile.setCounter(_counter);
		
		var _enemyMap:Map = _currentState._enemyMap;
		_enemyMap.redrawTile(p_tile);
		
		_currentState._enemyMap.removeSelectorTargets();
		
		var _array:Array = new Array([0,1],[0,-1],[1,0],[-1,0]);
		_currentState._enemyMap.selectTilesOpenShape(p_tile,_array);
		
		if (_currentState._status == _currentState._statusTypes["terrainDone"]) {
			_currentState._attackDone = true;
			finished();
		} else {
			var _status:Number = _currentState._statusTypes["terrainDone"];
			_currentState._status = _status;
		}
	}
	
	private function moveDemon(p_tile:Tile) {
		var _currentDemon:Demon = _currentState._currentDemon;
		var _currentTile:Tile = _currentState._currentTile;
		_currentDemon.setX(p_tile.getX());
		_currentDemon.setY(p_tile.getY());
		
		_currentTile.removeDemon();
		p_tile.addDemon(_currentDemon);

		var _playerMap:Map = _currentState._playerMap;
		
		_playerMap.redrawDemon(p_tile);
		
		//removeEverything();
		//removeBigPictures();
		_currentState._status = _currentState._statusTypes[_currentState._currentDemon.getOwner().getName()];
		_currentState._currentTile = p_tile;
		
		
		_currentState._moveDone = true;
		
		finished();
	}
	
	private function rangeAttack(p_attackingDemon:Demon, p_attack:Attack, p_enemyMap:Map):Void {
		
		// Select Attackable Enemies
		var _tiles:Array = p_enemyMap.getColumn(p_attackingDemon.getX());
		var _sideTilesLeft:Array = p_enemyMap.getColumn(p_attackingDemon.getX() - 1);
		var _sideTilesRight:Array = p_enemyMap.getColumn(p_attackingDemon.getX() + 1);
		var _foundEnemy:Boolean = false;
		var _foundEnemyLeft:Boolean = false;
		var _foundEnemyRight:Boolean = false;

		for (var i = 0; i < _tiles.length; i++) {
			var _tile:Tile = _tiles[i];
			var _sideTileLeft:Tile = _sideTilesLeft[i];
			var _sideTileRight:Tile = _sideTilesRight[i];
			
			if (_tile.getDemon() && _foundEnemy == false) {
				_foundEnemy = true;
				p_enemyMap.selectTargetDemons(_tile.getX(), _tile.getY(), "Full");
			}
			if (_sideTileLeft.getDemon() && _foundEnemyLeft == false) {
				_foundEnemyLeft = true;
				p_enemyMap.selectTargetDemons(_sideTileLeft.getX(), _sideTileLeft.getY(), "Half");
			
			}
			if (_sideTileRight.getDemon() && _foundEnemyRight == false) {
				_foundEnemyRight = true;
				p_enemyMap.selectTargetDemons(_sideTileRight.getX(), _sideTileRight.getY(), "Half");
			}
		}
	}
	
	private function meleeAttack(p_attackingDemon:Demon, p_attack:Attack, p_enemyMap:Map):Void {
		
		// Select Attackable Enemies
		var _tiles:Array = p_enemyMap.getColumn(p_attackingDemon.getX());
		var _sideTilesLeft:Array = p_enemyMap.getColumn(p_attackingDemon.getX() - 1);
		var _sideTilesRight:Array = p_enemyMap.getColumn(p_attackingDemon.getX() + 1);
		var _foundEnemy:Boolean = false;
		var _foundEnemyLeft:Boolean = false;
		var _foundEnemyRight:Boolean = false;

		// Melee attacks can only hit the back most enemy
		for (var i = (_tiles.length - 1); i >= 0; i--) {
			var _tile:Tile = _tiles[i];
			var _sideTileLeft:Tile = _sideTilesLeft[i];
			var _sideTileRight:Tile = _sideTilesRight[i];
			
			if (_tile.getDemon() && _foundEnemy == false) {
				_foundEnemy = true;
				_tile.setIsTarget(true);
				p_enemyMap.selectTargetDemons(_tile.getX(), _tile.getY(), "Full");
			}
			if (_sideTileLeft.getDemon() && _foundEnemyLeft == false) {
				_foundEnemyLeft = true;
				_sideTileLeft.setIsTarget(true);
				p_enemyMap.selectTargetDemons(_sideTileLeft.getX(), _sideTileLeft.getY(), "Half");
			
			}
			if (_sideTileRight.getDemon() && _foundEnemyRight == false) {
				_foundEnemyRight = true;
				_sideTileRight.setIsTarget(true);
				p_enemyMap.selectTargetDemons(_sideTileRight.getX(), _sideTileRight.getY(), "Half");
			}
		}
	}
	
	private function terrainAttack(p_attackingDemon:Demon, p_attack:TerrainAttack, p_enemyMap:Map):Void {
		p_enemyMap.selectTilesOpenToChange();
	}
	
	// *** Demon Selection Functions *** //
	/* The following functions are used to control
	 * demon selection.*/
	 
	public static function leftDemon(p_demon:Demon, p_tile:Tile):Void {
		selectDemon("Left", p_demon, _leftDemonDepth, 5, 0, p_tile);
	}
	
	public static function rightDemon(p_demon:Demon, p_tile:Tile):Void {
		selectDemon("Right", p_demon, _rightDemonDepth, 595, 0, p_tile);
	}
	
	public static function selectDemon(p_side:String, p_demon:Demon, p_depth:Number, p_x:Number, p_y:Number, p_tile:Tile):Void {
		var _imageName:String = "bigPic" + p_side;
		Graphics.loadAndDrawObject(_imageName, p_depth, "bigDemons/bigDemonBackground.png", p_x, p_y);
		Graphics.loadAndDrawObject(_imageName + "Pic", p_depth + 1, p_demon.graphicNameBig(), p_x + 25, p_y + 25);
		Graphics.loadAndDrawObject(_imageName + "Type", p_depth + 3, "typePieces/" + p_demon.getElementType() + "Tile.png", p_x + 153, p_y + 39);
		Graphics.loadAndDrawObject(_imageName + "Terrain", p_depth + 4, "typePieces/" + p_tile.getElementType() + "Tile.png", p_x + 153, p_y + 105);
		
		if (p_demon.hasTypeAdvantage(p_tile.getElementType())) {
			Graphics.loadAndDrawObject(_imageName + "TypeAdvantage", p_depth + 2, "bigDemons/goodTriangles.png", p_x, p_y);
		} else if (p_demon.hasTypeDisadvantage(p_tile.getElementType())) {
			Graphics.loadAndDrawObject(_imageName + "TypeAdvantage", p_depth + 2, "bigDemons/badTriangles.png", p_x, p_y);
		} else {
			Graphics.loadAndDrawObject(_imageName + "TypeAdvantage", p_depth + 2, "bigDemons/neutralTriangles.png", p_x, p_y);
		}
		
		
		// create side image
		var _picTextFormat:TextFormat = 
			new TextFormat("Impact", 20, 0xFFFFFF, true, false, false, null, null, "center");
		var _hpTextFormat:TextFormat = 
			new TextFormat("Impact", 16, 0xFFFFFF, false, false, false, null, null, "center");
		_root[_imageName].createEmptyMovieClip("statsText", 2);
		_root[_imageName]["statsText"]._x = 97;
		_root[_imageName]["statsText"]._y = 153;
		_root[_imageName]["statsText"].createTextField("demonName", 1, 0, 0, 100, 50);
		_root[_imageName]["statsText"].demonName.text = p_demon.getName();
		_root[_imageName]["statsText"].demonName.setTextFormat(_picTextFormat);
		_root[_imageName]["statsText"].demonName.selectable = false;
		_root[_imageName]["statsText"].createTextField("health", 2, 0, 21, 100, 30);
		_root[_imageName]["statsText"].health.text = p_demon.getHealth() + " / " + p_demon.getHealthFull();
		_root[_imageName]["statsText"].health.setTextFormat(_hpTextFormat);
		_root[_imageName]["statsText"].health.selectable = false;
		
		
		_root[p_demon.getOwner() + "Map"].select(p_demon.getX(), p_demon.getY());
		//trace("Demon name: " + p_demon.getName());
		
	}
	
	private static function removeDemonSelection(p_side:String):Void {
		var _imageName:String = "bigPic" + p_side;
		removeMovieClip(_imageName);
		removeMovieClip(_imageName + "Pic");
		removeMovieClip(_imageName + "TypeAdvantage");
		removeMovieClip(_imageName + "Type");
		removeMovieClip(_imageName + "Terrain");
	}
	
	// *** Menu Functions *** //
	/* The following functions are added as listeners
	 * to the buttons within the PlayerMenu class.  The
	 * names indicate what actions run these functions. */
	 
	public function attackSelection(p_demon:Demon, p_attack:Attack):Void {
		_currentState.printState();
		var _status:Number = _currentState._statusTypes["attack"];
		_currentState._status = _status;
		_currentState._actionAttack = p_attack;
		
		_currentState._enemyMap.removeSelectorTargets();
		//trace("attack is done " + _currentState._attackDone);
		
		if (p_attack.hasRangeType("range")) {
			rangeAttack(p_demon, p_attack, _currentState._enemyMap);
		} else if (p_attack.hasRangeType("melee")) {
			meleeAttack(p_demon, p_attack, _currentState._enemyMap);
		}
	}
	
	public function terrainAttackSelection(p_demon:Demon, p_terrainAttack:TerrainAttack):Void {
		var _status:Number = _currentState._statusTypes["terrainAttack"];
		_currentState._status = _status;
		_currentState._actionTerrainAttack = p_terrainAttack;
		
		_currentState._enemyMap.removeSelectorTargets();
		
		terrainAttack(p_demon, p_terrainAttack, _currentState._enemyMap);
	}
	
	public function moveSelection():Void {
		var _status:Number = _currentState._statusTypes["move"];
		_currentState._status = _status;
		var _currentDemon:Demon = _currentState._currentDemon;
		_currentState._playerMap.selectMovementTiles(_currentDemon.getX(), _currentDemon.getY(), _currentDemon.getMovement());
	}
	
	public function passSelection():Void {
		nextTurn();
	}
	
	public function removeEverything() {
		this._player1.getMap().removeSelectorTargets();
		this._player2.getMap().removeSelectorTargets();
	}
	
	public function removeBigPictures() {
		removeDemonSelection("Left");
		removeDemonSelection("Right");
	}
	
	// *** Tile Functions *** //
	/* The following functions are added as listeners
	 * to the tiles within the Map class.  The names
	 * indicate what actions run these functions. */
	 
	// Called when the mouse clicks on a tile
	public static function tileOnPress(p_tile:Tile):Void {
		if (p_tile.getIsTarget()) {
			switch (_root._battCont._currentState._status) {
				case _root._battCont._currentState._statusTypes["attack"]:
					_root._battCont.dealDamage(p_tile, _root._battCont._currentState._actionAttack);
				break;
				case _root._battCont._currentState._statusTypes["move"]:
					_root._battCont.moveDemon(p_tile);
				break;
				case _root._battCont._currentState._statusTypes["terrainAttack"]:
					_root._battCont.terrainChange(p_tile, _root._battCont._currentState._actionTerrainAttack);
				break;
				case _root._battCont._currentState._statusTypes["terrainDone"]:
					_root._battCont.terrainChange(p_tile, _root._battCont._currentState._actionTerrainAttack);
				break;
			}
		}
	}
	
	// Called when the mouse rolls over on a tile
	public static function tileOnOver(p_map:Map, p_tile:Tile):Void {
		if (p_tile.getDemon()) {						// evaluates to true if the tile contains a demon
			if (_root._battCont._currentState._status == _root._battCont._currentState._statusTypes["ai"]) {
				leftDemon(p_tile.getDemon(), p_tile);		// draw the demon on screen
			} else {
				rightDemon(p_tile.getDemon(), p_tile);		// draw the demon on screen
			}
			p_map.selectDemon(p_tile.getX(), p_tile.getY());
		} else {
			if (_root._battCont._currentState._currentPlayer == "ai") {
				removeDemonSelection("Left");
			} else {
				removeDemonSelection("Right");
			}
			p_map.removeSelector();
		}
	}
}